package net.hadto.cyberneticjamband;

import processing.core.*;
import cc.arduino.*;
import java.util.ArrayList;
import com.heatonresearch.book.introneuralnet.neural.hopfield.HopfieldNetwork;


public class CyberneticJamBand extends PApplet {
	// remember to add this line for full screen

	/*public static void main(String args[]) {
		PApplet.main(new String[] { "--present", "net.hadto.cyberneticjamband.CyberneticJamBand" });
	}*/

	/**
	 * 
	 */
	private static final long serialVersionUID = -750327829055912343L;


	/**
	 * Toy battle, YOU vs. CPU for the ultimate beat.
	 */
	
	//serial communication
	Arduino arduino;
	
	//state control
	public static final int BPM = 250; //250 millis = 40 BPM
	public static final int NUMBER_OF_BEATS = 40; //how many beats per turn
	public static final int IDLE = 0;
	public static final int MY_TURN = 1;
	public static final int CPU_TURN = 2;
	GameState gameState;
	
	boolean newTurn=false;

	int timePeriodCnt = 0;
	
	double lastMillis;
	
	//display
	int rows = 4;
	int cols = 4;
	float w;
	float h;
	float x = 0, y = 0;
	Square[] squares = new Square[16];
	
	Beat[] beats = new Beat[16];
	
	//this array keeps track of what squares to draw
	public boolean displayList[] = new boolean[16];
		
	//this array captures button presses for each time period
	public boolean oneBeatBuffer[] = new boolean[16];
	
	//this double array takes the buffer from each time period and stores it to be passed on at the end of the turn
//	public boolean myTurnDataOld[][] = new boolean[20][16];
	ArrayList<Beat> myTurnData = new ArrayList<Beat>();
	
	//array to catch the results from the neural network
	public boolean[] result;

	public void setup() {
		//size(screen.width,screen.height);
		size(400,400);

		//set turnBuffer to -1
		for(int i=0;i<oneBeatBuffer.length;i++) oneBeatBuffer[i]=false;
		myTurnDataInit();

		
		//serial communication
		arduino = new Arduino(this, Arduino.list()[0], 115200);
		  for (int i = 0; i <= 13; i++) arduino.pinMode(i, Arduino.INPUT);
		
		//state control
		gameState = new GameState();
		gameState.setGameState(IDLE);
		lastMillis = millis();
		
		
		//display
		w = width / cols;
		h = height / rows;
		for (int i = 0; i < squares.length; i++) {
			squares[i] = new Square(this,gameState, w, h, x, y);
			squares[i].display();
			if (x > width - (w * 2)) 
			{
				x = 0;
				y += h;
			} 
			else
				x += w;
		}
	}

	public void draw() 
	{
		// The deltaTime variable stores how many beats per minute have elapsed since the last time draw() was called
		double currentMillis = millis();
		double bpmTime = (currentMillis - lastMillis) / BPM;
		
		
		switch(gameState.getGameState())
		{
		
		case IDLE:
			idleLoop();
			break;
		
		case MY_TURN:
			if(bpmTime>=1)
			{
				lastMillis = currentMillis;	
				myTurnUpdate();
				timePeriodCnt++;
			}
			else getButtonPresses();
			
			if(timePeriodCnt>=NUMBER_OF_BEATS)
			{
				timePeriodCnt=0;
				gameState.setGameState(CPU_TURN);
			}
			break;
			
		case CPU_TURN:
			if(bpmTime>=1)
			{
				lastMillis = currentMillis;
				cpuTurnUpdate();
				timePeriodCnt++;
			}
			
			if(timePeriodCnt>=NUMBER_OF_BEATS)
			{
				timePeriodCnt=0;
				myTurnDataClear();
				myTurnDataInit();
				gameState.setGameState(MY_TURN);				
			}
			break;
		}	
			
		updateScreen();
	
	}
	
	/**
	 * this is the function that manages the graphics 
	 */
	public void updateScreen()
	{
		//this is where we draw to the screen based on displayList values. 
		for(int i=0;i<squares.length;i++)
		{
			squares[i].state = displayList[i];
			squares[i].display();
		}	
	}
	
	/**
	 * this will initialize myTurnData to all false
	 */
	public void myTurnDataInit()
	{
		for(int i = 0;i<=NUMBER_OF_BEATS-1;i++) myTurnData.add(new Beat());
	}
	
	/**
	 * this will empty out myTurnData to start a fresh round of MY_TURN
	 */
	public void myTurnDataClear()
	{
		myTurnData.clear();
	}


	/**
	 * WHILE currentState == IDLE
	 * this is where the game starts and goes 
	 * when no input is received for one turn
	 * when input is received change currentState to MYTURN and stateChanged to TRUE
	 * 
	 */
	public void idleLoop()
	{
		getButtonPresses();
		for(int i =0;i<oneBeatBuffer.length;i++)
			if(oneBeatBuffer[i]==true) gameState.setGameState(MY_TURN);
	}
	
	

	
	
	/**
	 * this gets called every time the BPM timer task gets called when constant MYTURN is true
	 * this is responsible for filling in the individual time period data into the turn buffer 
	 * and flushing the timePeriodBuffer between this call and the last the timePeriodBuffer 
	 * is by default false and if a button press is registered in the main loop changed to true
	 * after the flush all positions in the timePeriodBuffer is set back to false
	 */
	public void myTurnUpdate()
	{
		//try this instead
		displayList = myTurnData.get(timePeriodCnt).getValue();
		
		println("myTurnUpdate: "+timePeriodCnt);

	}
	

	

	/**
	 * WHILE currentState == CPUTURN
	 * pass the myTurnObject over to cpuTurnLoop
	 * start a timer repeating for 0.5 seconds
	 * iterate over the myTurnObject
	 * take out each array and feed it to the HopField Network
	 * first as a 
	 * 
	 */
	public void cpuTurnUpdate()
	{		
		//network
		final HopfieldNetwork network = new HopfieldNetwork(16);
		
		network.train(myTurnData.get(timePeriodCnt).getValue());
		
		result = network.present(myTurnData.get(timePeriodCnt).getValue());
		
		displayList = result;
		
		println("cpuTurnUpdate: "+ timePeriodCnt);
		
	}
	
	/**
	 * this will capture the button presses into the array timePeriodBuffer
	 */
	public void getButtonPresses()
	{
		for (int i = 2; i <= 13; i++) 
		{
			if (arduino.digitalRead(i) == Arduino.HIGH)
			{
				oneBeatBuffer[i-2]=true;
				myTurnData.get(timePeriodCnt).setValue(i-2,true);
			}
		}
		for (int i = 0; i<=3; i++)
		{
			if(arduino.analogRead(i)!=0)
			{
				oneBeatBuffer[i+12]=true;
				myTurnData.get(timePeriodCnt).setValue(i+12,true);
			}
		}
	}
	

}	



